package com.cmz.common.providers;

import com.cmz.common.api.dto.StandardSubscriber;
import com.cmz.common.api.enums.ParamDefEnum;
import com.cmz.common.api.enums.StandardSubscriberParam;
import com.cmz.common.api.providers.DataSourceProvider;
import com.cmz.common.api.providers.KeychainProvider;
import com.cmz.common.api.providers.LocaleProvider;
import com.cmz.common.api.providers.ParamProvider;
import com.cmz.common.api.providers.SubscriberProvider;
import com.cmz.common.enums.CommonError;
import com.cmz.common.exception.ServiceRuntimeException;
import com.cmz.common.utils.StringUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * 信息提供者工厂
 * @author cmz
 */
@Slf4j
@Getter
public class ProviderFactory {

    private ProviderFactory() {}

    private final static ProviderFactory SINGLETON = new ProviderFactory();

    public final static String ANONYMOUS_SID      = "ANONYMOUS";

    public final static String ANONYMOUS_USERNAME = "Anonymous";

    /** 是否允许匿名访问 */
    boolean anonymousAccessAllowed = true;

    /** 用户信息提供者 */
    SubscriberProvider specificSubscriberProvider;

    /** 用户参数提供者 */
    ParamProvider subscriberParamProvider;

    /** 全局参数提供者 */
    ParamProvider globalParamProvider;

    /** UI参数提供者 */
    ParamProvider uiParamProvider;

    /** 客户端语言区域信息提供者 */
    LocaleProvider clientLocaleProvider;

    /** 服务端语言区域信息提供者 */
    LocaleProvider serverLocaleProvider;

    /** 秘钥链提供者 */
    KeychainProvider keychainProvider;

    /** 数据源提供者 */
    DataSourceProvider dataSourceProvider;

    @Deprecated
    public static ProviderFactory newProviderFactory() {
        return SINGLETON;
    }

    public static ProviderFactory getInstance() {
        return SINGLETON;
    }

    public ProviderFactory setSubscriberProvider(SubscriberProvider specificSubscriberProvider) {
        this.specificSubscriberProvider = specificSubscriberProvider;
        return this;
    }

    public ProviderFactory setClientLocaleProvider(LocaleProvider localeProvider) {
        this.clientLocaleProvider = localeProvider;
        return this;
    }

    public ProviderFactory setServerLocaleProvider(LocaleProvider localeProvider) {
        this.serverLocaleProvider = localeProvider;
        return this;
    }

    public ProviderFactory setSubscriberParamProvider(ParamProvider paramProvider) {
        this.subscriberParamProvider = paramProvider;
        return this;
    }

    public ProviderFactory setGlobalParamProvider(ParamProvider paramProvider) {
        this.globalParamProvider = paramProvider;
        return this;
    }

    public ProviderFactory setUiParamProvider(ParamProvider paramProvider) {
        this.uiParamProvider = paramProvider;
        return this;
    }

    public ProviderFactory setKeychainProvider(KeychainProvider keychainProvider) {
        this.keychainProvider = keychainProvider;
        return this;
    }

    public ProviderFactory setDataSourceProvider(DataSourceProvider dataSourceProvider) {
        this.dataSourceProvider = dataSourceProvider;
        return this;
    }

    public static SubscriberProvider getSubscriberProvider() {
        return SINGLETON.getSpecificSubscriberProvider();
    }

    public static Locale getClientLocale() {
        return SINGLETON.getClientLocaleProvider().getLocale();
    }

    public static Locale getServerLocale() {
        return SINGLETON.getServerLocaleProvider().getLocale();
    }

    public static String getCurrentSubject() {
        if (SINGLETON.getSpecificSubscriberProvider()!=null) {
            try {
                String sid = SINGLETON.getSpecificSubscriberProvider().getSid();
                if (StringUtils.isEmpty(sid)) {
                    throw new ServiceRuntimeException(CommonError.XATSDKCM0000);
                }
                return sid;
            } catch (Exception e) {
                if (!SINGLETON.isAnonymousAccessAllowed()) {
                    throw e;
                }
            }
        }

        return getAnonymousSubscriber().getSid();
    }

    public static String getCurrentName(String sid) {
        if (SINGLETON.getSpecificSubscriberProvider()!=null) {
            try {
                String name = SINGLETON.getSpecificSubscriberProvider().getSubscriber(sid).getUsername();
                if (StringUtils.isEmpty(name)) {
                    throw new ServiceRuntimeException(CommonError.XATSDKCM0000);
                }
                return name;
            } catch (Exception e) {
                if (!SINGLETON.isAnonymousAccessAllowed()) {
                    throw e;
                }
            }
        }

        return getAnonymousSubscriber().getUsername();
    }

    public static Object getSubscriberParam(ParamDefEnum param) {
        return getParam(SINGLETON.getSubscriberParamProvider(), param);
    }

    public static Object getGlobalParam(ParamDefEnum param) {
        return getParam(SINGLETON.getGlobalParamProvider(), param);
    }

    public static Object getUiParam(ParamDefEnum param) {
        return getParam(SINGLETON.getUiParamProvider(), param);
    }

    public static KeychainProvider getKeychainProvider() {
        return SINGLETON.keychainProvider;
    }

    private static Object getParam(ParamProvider paramProvider, ParamDefEnum param) {
        if (paramProvider==null) {
            throw new ServiceRuntimeException(CommonError.XATSDKCM0000);
        }

        try {
            return paramProvider.getParam(param);
        } catch (Exception e) {
            log.warn("Error retrieving param '{}' from param provider '{}': {}", param, paramProvider, e);
            return null;
        }
    }

    public static DataSourceProvider getDataSourceProvider() {
        return SINGLETON.dataSourceProvider;
    }

    private static StandardSubscriber getAnonymousSubscriber() {
        return new StandardSubscriber() {
            @Override
            public String getSid() {
                return ANONYMOUS_SID;
            }

            @Override
            public String getUsername() {
                return ANONYMOUS_USERNAME;
            }

            @Override
            public String getSource() {
                return "VOID";
            }

            @Override
            public Map<String, Object> getParams() {
                return null;
            }

            @Override
            public Object getParam(StandardSubscriberParam subscriberParam) {
                return null;
            }

            @Override
            public Set<String> getPerms() {
                return null;
            }
        };
    }

}
